package com.summer.data;

//import com.summer.data.connection.Session;
//import com.summer.data.connection.SessionFactory;
import com.summer.data.dialect.Dialect;
import com.summer.data.dialect.DialectCenter;
import com.summer.data.jql.Context;
import com.summer.data.jql.behavior.*;
import com.summer.data.logging.Logger;
import com.summer.data.parameter.ParameterStruct;
import com.summer.data.rs.datatype.PageInfo;

import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

/**
 * @author 345480567@qq.com
 */

public class Jsql implements SelectAble, FilterAble, GroupAble, HavingAble, OrderAble {

    // 标志变量：是否已经添加了where
    private boolean addedWhere = false;

    StringBuffer stringBuffer = new StringBuffer();

    public List<ParameterStruct> parameterStructList = new ArrayList<>();

    private PageInfo pageInfo;

    public static Jsql create() {
        return new Jsql();
    }

    public Jsql() {
        this.pageInfo = PageInfo.get();
    }

    public Jsql(String sql) {
        stringBuffer.append(sql);
        this.pageInfo = PageInfo.get();
    }

    // public Jsql(String sql, Object[] parameters) {
    // stringBuffer.append(sql);
    // appendParameterList(parameterList);
    // this.pageInfo = PageInfo.get();
    // }

    public Jsql(String sql, Object... parameters) {
        stringBuffer.append(sql);
        appendParameters(parameters);
        this.pageInfo = PageInfo.get();
    }

    public Jsql(String sql, List<ParameterStruct> parameterList) {
        stringBuffer.append(sql);
        appendParameterList(parameterList);
        this.pageInfo = PageInfo.get();
    }

    public Jsql append(String sqlString, Object... values) {
        stringBuffer.append(sqlString);
        appendParameters(ParameterStruct.parameters(values));
        return this;
    }

    public Jsql append(String sqlString, Object value, JDBCType jdbcType) {
        stringBuffer.append(sqlString);
        parameterStructList.add(new ParameterStruct(value, jdbcType));
        return this;
    }

    public Jsql appendParameter(Object value, JDBCType jdbcType) {
        this.parameterStructList.add(new ParameterStruct(value, jdbcType));
        return this;
    }

    public Jsql appendParameters(Object... values) {
        for (Object value : values) {
            if (value instanceof ParameterStruct) {
                this.parameterStructList.add((ParameterStruct) value);
            } else {
                this.parameterStructList.add(new ParameterStruct(value));
            }
        }
        return this;
    }

    public Jsql append(String sqlString, ParameterStruct... parameterStructs) {
        stringBuffer.append(sqlString);
        appendParameters(parameterStructs);
        return this;
    }

    public Jsql appendParameterList(List<ParameterStruct> parameterList) {
        for (ParameterStruct value : parameterList) {
            this.parameterStructList.add(value);
        }
        return this;
    }

    public Jsql append(Jsql jsql) {
        stringBuffer.append(jsql.stringBuffer.toString());
        jsql.parameterStructList.forEach(parameter -> {
            this.parameterStructList.add(parameter);
        });
        return this;
    }

    public Jsql appendWhere(String sqlString, Object... parameters) {
        String lowCaseSqlString = sqlString.toLowerCase();
        String trimLowCaseSqlString = lowCaseSqlString.trim();
        if (!addedWhere) {
            addedWhere = true;
            stringBuffer.append(" WHERE ");
            if (trimLowCaseSqlString.startsWith("and ")) {
                int indexOf = lowCaseSqlString.indexOf("and ");
                stringBuffer.append(sqlString.substring(indexOf+4));
            } else if (trimLowCaseSqlString.startsWith("or ")) {
                int indexOf = lowCaseSqlString.indexOf("or ");
                stringBuffer.append(sqlString.substring(indexOf+3));
            } else {
                stringBuffer.append(sqlString);
            }
        } else {
            stringBuffer.append(sqlString);
        }
        appendParameters(parameters);
        return this;
    }

    public Jsql appendWhere(Jsql jsql) {
        appendWhere(jsql.stringBuffer.toString());
        jsql.parameterStructList.forEach(parameter -> {
            this.parameterStructList.add(parameter);
        });
        return this;
    }

    public String getString() {
        return stringBuffer.toString();
    }

    public List<ParameterStruct> getParameterList() {
        return parameterStructList;
    }

    public static void format(Jsql jsql, String format, Object... args) {
        // args 中 Sql 类型先转化成 String，
        // 转化后的 String 放置到 objects
        // 最后统一格式化
        Object[] objects = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof Jsql) {
                // Sql 转化成 String
                Jsql jsqlObj = (Jsql) arg;
                objects[i] = jsqlObj.getString();
                // 参数放置到 sql 中
                jsql.parameterStructList.addAll(jsqlObj.getParameterList());
            } else {
                objects[i] = arg;
            }
        }

        // 统一格式化
        Formatter formatter = new Formatter(jsql.stringBuffer);
        formatter.format(format, objects);
    }

    public static Jsql format(String format, Object... args) {
        Jsql jsql = new Jsql();
        format(jsql, format, args);
        return jsql;
    }

    public int sqlLength() {
        return stringBuffer.length();
    }

    public Jsql setPage(Integer pageNo, Integer pageSize) {
        assert pageNo > 0;
        assert pageSize > 0;
        pageInfo = new PageInfo(pageNo, pageSize);
        return this;
    }

    public PageInfo getPageInfo() {
        return pageInfo;
    }

    public void print(Dialect dialect) {
        Logger.log("==> SQL: \r\n%s\r\n", stringBuffer.toString());
        if (parameterStructList.size() > 0) {
            Logger.log("==> parameters: \r\n");
            for (ParameterStruct parameterStruct : parameterStructList) {
                Logger.log("==>%s (%s) \r\n", parameterStruct.value, dialect.getParameterJDBCType(parameterStruct));
            }
        }
    }

    @Override
    public Jsql toSelectJsql(Context ctx, Dialect dialect) {
        return this;
    }

    @Override
    public Jsql toFilterJsql(Context ctx, Dialect dialect) {
        return this;
    }

    @Override
    public Jsql toGroupSql(Context ctx, Dialect dialect) {
        return this;
    }

    @Override
    public Jsql toHavingSql(Dialect dialect) {
        return this;
    }

    @Override
    public Jsql toOrderSql(Context ctx, Dialect dialect) {
        return this;
    }
}
